.global _start  				/* 全局标号 */

/*
 * 描述：	_start函数，首先是中断向量表的创建
 * 参考文档:ARM Cortex-A(armV7)编程手册V4.0.pdf P42，3 ARM Processor Modes and Registers（ARM处理器模型和寄存器）
 * 		 	ARM Cortex-A(armV7)编程手册V4.0.pdf P165 11.1.1 Exception priorities(异常)
 */
_start:
    /* 来自ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf  的exception handling章节的中断表*/
	ldr pc, =Reset_Handler		/* 复位中断 					*/	
	ldr pc, =Undefined_Handler	/* 未定义中断 					*/
	ldr pc, =SVC_Handler		/* SVC(Supervisor)中断 		*/
	ldr pc, =PrefAbort_Handler	/* 预取终止中断 					*/
	ldr pc, =DataAbort_Handler	/* 数据终止中断 					*/
	ldr	pc, =NotUsed_Handler	/* 未使用中断					*/
	ldr pc, =IRQ_Handler		/* IRQ中断 					*/
	ldr pc, =FIQ_Handler		/* FIQ(快速中断)未定义中断 			*/

/* 复位中断 */	
Reset_Handler:

	cpsid i						/* 关闭全局中断 在ARM Cortex-A(armV7)编程手册V4.0.pdf的例子代码可找到此指令*/

	/* 关闭I,DCache和MMU  ，为了确保运行环境
	 * 采取读-改-写的方式。
	 */

/*CP15协处理器作用
系统控制：包括处理器工作模式、权限级别、异常处理等 
内存管理：负责管理内存地址空间、虚拟地址到物理地址的转换、内存保护等 
缓存控制：控制数据缓存和指令缓存的开启和关闭、缓存的清除和刷新等操作 
MMU（内存管理单元）的配置和管理：包括页表的设置、转换表的控制等 
性能监控：提供系统性能监控功能，如缓存命中、未命中等事件的计数 
 */
    //MRC{cond} p15, <opcode_1>, <Rd>, <CRn>, <CRm>, <opcode_2>
	mrc     p15, 0, r0, c1, c0, 0     /* 读取CP15的C1寄存器到R0中       SCTLR寄存器 32-bit System Control Register	    在ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf    	*/
    bic     r0,  r0, #(0x1 << 12)     /* 清除C1寄存器的bit12位(I位)，关闭I Cache            	*/
    bic     r0,  r0, #(0x1 <<  2)     /* 清除C1寄存器的bit2(C位)，关闭D Cache    				*/
    bic     r0,  r0, #0x2             /* 清除C1寄存器的bit1(A位)，关闭对齐						*/
    bic     r0,  r0, #(0x1 << 11)     /* 清除C1寄存器的bit11(Z位)，关闭分支预测					*/
    bic     r0,  r0, #0x1             /* 清除C1寄存器的bit0(M位)，关闭MMU				       	*/
    mcr     p15, 0, r0, c1, c0, 0     /* 将r0寄存器中的值写入到CP15的C1寄存器中	 				*/

	
#if 0
	/* 汇编版本设置中断向量表偏移 */
	ldr r0, =0X87800000

	dsb     //Data Synchronization Barrier
	isb     //Instruction Synchronization Barrier       这两条在ARM Cortex-A(armV7)编程手册V4.0.pdf有说明
	mcr p15, 0, r0, c12, c0, 0  /*VBARd寄存器 32-bit Vector Base Address Register 在ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf */
	dsb
	isb
#endif
    /*各模式的参考值在ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf的B1.3.3章节CPSR寄存器 */
	/* 设置各个模式下的栈指针，
	 * 注意：IMX6UL的堆栈是向下增长的！
	 * 堆栈指针地址一定要是4字节地址对齐的！！！
	 * DDR范围:0X80000000~0X9FFFFFFF
	 */
	/* 进入IRQ模式 */
	mrs r0, cpsr
	bic r0, r0, #0x1f 	/* 将r0寄存器中的低5位清零，也就是cpsr的M0~M4 	*/
	orr r0, r0, #0x12 	/* r0或上0x13,表示使用IRQ模式	*/
	msr cpsr, r0		/* 将r0 的数据写入到cpsr_c中 					*/
	ldr sp, =0x80600000	/* 设置IRQ模式下的栈首地址为0X80600000,大小为2MB */

	/* 进入SYS模式 */
	mrs r0, cpsr
	bic r0, r0, #0x1f 	/* 将r0寄存器中的低5位清零，也就是cpsr的M0~M4 	*/
	orr r0, r0, #0x1f 	/* r0或上0x13,表示使用SYS模式					*/
	msr cpsr, r0		/* 将r0 的数据写入到cpsr_c中 					*/
	ldr sp, =0x80400000	/* 设置SYS模式下的栈首地址为0X80400000,大小为2MB */

	/* 进入SVC模式 */
	mrs r0, cpsr
	bic r0, r0, #0x1f 	/* 将r0寄存器中的低5位清零，也就是cpsr的M0~M4 	*/
	orr r0, r0, #0x13 	/* r0或上0x13,表示使用SVC模式					*/
	msr cpsr, r0		/* 将r0 的数据写入到cpsr_c中 					*/
	ldr sp, =0X80200000	/* 设置SVC模式下的栈首地址为0X80200000,大小为2MB */

	cpsie i				/* 打开全局中断 在ARM Cortex-A(armV7)编程手册V4.0.pdf的例子代码可找到此指令*/
#if 0
	/* 使能IRQ中断 */
	mrs r0, cpsr		/* 读取cpsr寄存器值到r0中 			*/
	bic r0, r0, #0x80	/* 将r0寄存器中bit7清零，也就是CPSR中的I位清零，表示允许IRQ中断         I, bit[7] IRQ mask bit.*/
	msr cpsr, r0		/* 将r0重新写入到cpsr中 			*/
#endif

	b main				/* 跳转到main函数 			 	*/

/* 未定义中断 */
Undefined_Handler:
	ldr r0, =Undefined_Handler
	bx r0

/* SVC中断 */
SVC_Handler:
	ldr r0, =SVC_Handler
	bx r0

/* 预取终止中断 */
PrefAbort_Handler:
	ldr r0, =PrefAbort_Handler	
	bx r0

/* 数据终止中断 */
DataAbort_Handler:
	ldr r0, =DataAbort_Handler
	bx r0

/* 未使用的中断 */
NotUsed_Handler:

	ldr r0, =NotUsed_Handler
	bx r0

/* IRQ中断！重点！！！！！ */
IRQ_Handler:

	push {lr}					/* 保存lr地址 链接寄存器通常存储着断点*/
	push {r0-r3, r12}			/* 保存r0-r3，r12寄存器  cpu切换现场时自动保存4-11寄存器，剩下需要手动保存，具体参考freertos线程调度*/
    

/*程序状态寄存器（SPSR）的目的是记录异常前的当前程序状态寄存器（CPSR）的值。在发生异常时，CPSR 的值会被复制到所进入的模式的 SPSR 中。保存这个值意味着异常处理程序可以：
                1、在异常返回时，将 CPSR 恢复到异常发生前立即拥有的值。
                2、检查 CPSR 在异常发生时的值，例如，确定导致未定义指令异常的指令被执行时的指令集状态和权限级别。 */
	
    mrs r0, spsr				/* 读取spsr寄存器 ，如上所述spsr记录中断发生前的CPSR值*/
	push {r0}					/* 保存spsr寄存器 */

    // <opcode_1>, <Rd>, <CRn>, <CRm>, <opcode_2>
	mrc p15, 4, r1, c15, c0, 0 /* 从CP15的C0寄存器内的值到R1寄存器中，获取到的是GIC基地址
								* 参考文档ARM Cortex-A(armV7)编程手册V4.0.pdf P49
								* Cortex-A7 Technical ReferenceManua.pdf P68 P138--->指令在这页。。。
								*/							
	add r1, r1, #0X2000			/* GIC基地址加0X2000，也就是GIC的CPU接口端基地址   这在Cortex-A7 Technical ReferenceManua.pdf手册GIC部分，官方自己说的基于xx地址+offset*/
	ldr r0, [r1, #0XC]			/* GIC的CPU接口端基地址加0X0C就是GICC_IAR寄存器，偏移说明在Cortex-A7 Technical ReferenceManua.pdf，但是具体寄存器在ARM Generic Interrupt Controller(ARM GIC控制器)V2.0.pdf
								 * GICC_IAR寄存器保存这当前发生中断的中断号，我们要根据
								 * 这个中断号来决定调用哪个中断服务函数。
                                 这条指令的意思是将 r1 寄存器的值作为一个地址，
                                再加上偏移量 0XC（16进制的12），从那个内存地址中读取数据，然后将读取的数据存储到 r0 寄存器中。
								 */
	push {r0, r1}				/* 保存r0,r1        r0此时存着中断号，而r1存储着GIC的CPU接口端基地址*/
	
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                            /*至此！都是在做相同的操作：PUSH需要的数据入栈保存(保存进入中断之前的函数的CUP现场以及必要数据)，下面可以进入真正的IRQ_Handler逻辑了 */
	cps #0x13					/* 进入SVC模式，允许其他中断再次进去*/
    //push /pop {lr}大概因为进入了新模式吧
	push {lr}					/* 保存SVC模式的lr寄存器  ，这里保存lr是因为system_irqhandler函数运行完会覆盖掉lr，stm32的freertos也有此设计*/
                                            /*链接寄存器（lr）通常用来存储返回地址，这样在函数执行完毕后可以返回到调用它的代码位置继续执行。 */
	ldr r2, =system_irqhandler	/* 加载C语言中断处理函数到r2寄存器中*/
	blx r2						/* 运行C语言中断处理函数，带有一个参数，保存在R0寄存器中 */
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                                        /*运行完RQ_Handler逻辑该恢复CPU现场了*/

	pop {lr}					/* 执行完C语言中断服务函数，lr出栈 */
	cps #0x12					/* 进入IRQ模式 */
	pop {r0, r1}				
	str r0, [r1, #0X10]			/* 中断执行完成，写EOIR */

	pop {r0}						
	msr spsr_cxsf, r0			/* 恢复spsr */

	pop {r0-r3, r12}			/* r0-r3,r12出栈 */
	pop {lr}					/* lr出栈 */
	subs pc, lr, #4				/* 将lr-4赋给pc */
	
//从stm32的freertos线程切换到此中断函数来看，ARM涉及CPU现场切换的地方大体思路：保存现场（入栈），执行逻辑，恢复现场（出栈）

/* FIQ中断 */
FIQ_Handler:

	ldr r0, =FIQ_Handler	
	bx r0									

